package org.rkuk.kasutil.media 
{
	import flash.display.Shape;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundLoaderContext;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	
	
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.READY
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.READY
	 */
	[Event(name = "ready", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.PLAY
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.PLAY
	 */
	[Event(name = "play", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.STOP
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.STOP
	 */
	[Event(name = "stop", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.PAUSE
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.PAUSE
	 */
	[Event(name = "pause", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.RESUME
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.RESUME
	 */
	[Event(name = "resume", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.SEEK
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.SEEK
	 */
	[Event(name = "seek", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.END
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.END
	 */
	[Event(name = "end", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.BUFFER
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.BUFFER
	 */
	[Event(name = "buffer", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * @copy		org.rkuk.kasutil.media.MediaSourceEvent.RESET
	 * @eventType	org.rkuk.kasutil.media.MediaSourceEvent.RESET
	 */
	[Event(name = "reset", type = "org.rkuk.kasutil.media.MediaSourceEvent")]
	/**
	 * 扩充了flash.media.Sound的功能和事件，将控制flash.media.SoundChannel的功能集成到其中，并提供简明的播放状态事件.
	 * 由于MediaSound继承自flash.media.Sound，所以其可以代替Sound使用，需要注意其余Sound的区别是，MediaSound.play方法不会返回SoundChannel对象，
	 * 因为MediaSound自身已经集成了对SoundChannel的控制。
	 * @author rkuk
	 */
	public class MediaSound extends Sound implements IMediaSource 
	{
		private static const STATUS_PLAY:String = "statusPlay";
		private static const STATUS_STOP:String = "statusStop";
		private static const STATUS_PAUSE:String = "statusPause";
		private static const STATUS_BUFFER:String = "statusBuffer";
		private static const clock:Shape = new Shape();
		
		private var isDisposed:Boolean;
		private var context:SoundLoaderContext;
		private var soundTransform:SoundTransform;
		private var channel:SoundChannel;
		private var currentTime:Number;
		private var soundUrl:String;
		private var loop:int;
		
		
		/**
		 * 表示MediaStream当前的播放状态，可能的状态有四种：
		 * STATUS_PLAY:正在播放
		 * STATUS_STOP:停止
		 * STATUS_PAUSE:暂停
		 * STATUS_BUFFER:正在缓冲
		 */
		private var soundStatus:String;
		
		/**
		 * 创建MediaSound对象的构造函数
		 * @param	stream			地址为要加载的音频文件的URLRequest
		 * @param	context			加载音频时，默认使用的SoundLoaderContext对象。
		 * @param	soundTransform	播放音频时，作用到SoundChannel的SoundTransform对象。
		 * @see	flash.media.SoundChannel
		 * @see	flash.media.SoundTransform
		 * @see	flash.media.SoundLoaderContext
		 */
		public function MediaSound(stream:URLRequest=null, context:SoundLoaderContext = null, soundTransform:SoundTransform = null)
		{
			super(stream, context);
			if(stream!=null)
				soundUrl = stream.url;
			this.context = context;
			this.soundTransform = soundTransform;
			currentTime = 0;
			soundStatus = STATUS_STOP;
		}
		
		/**
		 * @inheritDoc
		 * @see	flash.media.Sound.load()
		 */
		override public function load(stream:URLRequest, context:SoundLoaderContext = null):void 
		{
			if (stream!=null && soundUrl != stream.url)
			{
				soundUrl = stream.url;
				addEventListener(ProgressEvent.PROGRESS, testSoundReady);
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.RESET));
				super.load(stream, context || this.context);
			}
		}
		
		private function testSoundReady(e:ProgressEvent):void
		{
			if (!isBuffering)
			{
				removeEventListener(ProgressEvent.PROGRESS, testSoundReady);
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.READY));
			}
		}
		
		/* INTERFACE org.rkuk.kasutil.media.IMediaSource */
		
		/**
		 * @inheritDoc
		 */
		public function dispose():void 
		{
			if (!isDisposed)
			{
				if(hasEventListener(ProgressEvent.PROGRESS))
					removeEventListener(ProgressEvent.PROGRESS, testSoundReady);
					
				stopTestBuffer();
				stopChannel();
				context = null;
				soundTransform = null;
				currentTime = 0;
				isDisposed = true;
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.DISPOSE));
			}
		}
		
		/**
		 * 功能和参数与play方法相同，提供IMediaSource接口的实现
		 * @see	#play()
		 */
		public function start(...rest):void 
		{
			play.apply(this, rest);
		}
		
		/**
		 * 操作音频开始播放.
		 * @param	startTime		播放起始时刻
		 * @param	loops			播放循环次数
		 * @param	sndTransform	播放时做到用到SoundChannel的SoundTransform对象
		 * @return	null			不像flash.media.Sound一样范围SoundChannel，因此MediaSound本身集成了对SoundChannel的控制。
		 * @see	flash.media.Sound.play()
		 */
		override public function play(startTime:Number = 0, loops:int = 0, sndTransform:SoundTransform = null):SoundChannel 
		{
			if (!isDisposed)
			{
				if (soundStatus==STATUS_PLAY || soundStatus==STATUS_BUFFER)
					stopChannel();
					
				currentTime = startTime;
				loop = loops;
				soundTransform = sndTransform;
				soundStatus = STATUS_PLAY;
				startTestBuffer();
				startChannel();
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.PLAY));
			}
			
			return null;
		}
		
		/**
		 * @inheritDoc
		 * @see	flash.media.SoundChannel.stop()
		 */
		public function stop():void 
		{
			if (!isDisposed && soundStatus != STATUS_STOP)
			{
				currentTime = 0;
				soundStatus = STATUS_STOP;
				stopTestBuffer();
				stopChannel();
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.STOP));
			}
		}
		
		/**
		 * @inheritDoc
		 */
		public function resume():void 
		{
			if (!isDisposed && soundStatus == STATUS_PAUSE)
			{
				soundStatus = STATUS_PLAY;
				startTestBuffer();
				startChannel();
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.RESUME));
			}
		}
		
		/**
		 * @inheritDoc
		 */
		public function pause():void 
		{
			if (!isDisposed && (soundStatus == STATUS_PLAY || soundStatus == STATUS_BUFFER))
			{
				currentTime = time;
				soundStatus = STATUS_PAUSE;
				stopTestBuffer();
				stopChannel();
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.PAUSE));
			}
		}
		
		/**
		 * @inheritDoc
		 */
		public function seek(offset:Number):void 
		{
			if (!isDisposed && soundStatus != STATUS_STOP && offset > 0 && offset < duration)
			{
				currentTime = offset;
				if (soundStatus != STATUS_PAUSE)
				{
					stopChannel();
					startChannel();
				}
				dispatchEvent(new MediaSourceEvent(MediaSourceEvent.SEEK));
			}
		}
		
		private function startChannel():void
		{
			channel = super.play(currentTime * 1000, loop, soundTransform);
			channel.addEventListener(Event.SOUND_COMPLETE, notifyEnd);
		}
		
		private function stopChannel():void
		{
			if (channel != null)
			{
				channel.removeEventListener(Event.SOUND_COMPLETE, notifyEnd);
				channel.stop();
				channel = null
			}
		}
		
		private function notifyEnd(e:Event):void
		{
			channel.removeEventListener(Event.SOUND_COMPLETE, notifyEnd);
			channel = null;
			stopTestBuffer();
			soundStatus = STATUS_STOP;
			currentTime = 0;
			dispatchEvent(new MediaSourceEvent(MediaSourceEvent.END));
		}
		
		/**
		 * @inheritDoc
		 */
		public function get time():Number 
		{
			if (!isDisposed && (soundStatus == STATUS_PLAY || soundStatus == STATUS_BUFFER))
				return channel.position / 1000;
				
			return currentTime;
		}
		
		/**
		 * @inheritDoc
		 */
		public function get duration():Number 
		{
			if (!isDisposed)
			{
				var percent:Number = bytesLoaded / bytesTotal * 1000;
				return length / percent;
			}
			
			return NaN;
		}
		
		private var isTestingBuffer:Boolean = false;
		private function startTestBuffer():void
		{
			if (!isTestingBuffer)
			{
				isTestingBuffer = true;
				clock.addEventListener(Event.ENTER_FRAME, testBufferAndEnd);
			}
		}
		
		private function stopTestBuffer():void
		{
			if (isTestingBuffer)
			{
				isTestingBuffer = false;
				clock.removeEventListener(Event.ENTER_FRAME, testBufferAndEnd);
			}
		}
		
		private function testBufferAndEnd(e:Event):void
		{
			if (isBuffering)
			{
				if (soundStatus == STATUS_PLAY)
				{
					soundStatus = STATUS_BUFFER;
					dispatchEvent(new MediaSourceEvent(MediaSourceEvent.BUFFER));
				}
			}
			else
			{
				if (soundStatus == STATUS_BUFFER)
				{
					soundStatus = STATUS_PLAY;
					dispatchEvent(new MediaSourceEvent(MediaSourceEvent.RESUME));
				}
			}
		}
		
		//TODO:增加设置soundTransform的功能
	}
}